﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Timers;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Audio;
using System.Data;
using PokeData;

namespace PokeTD
{
    class Wave
    {
        private int _waveID;
        private string _Name;
        private int _creepsToSpawn;
        private bool _waveStarted = false;
        private bool _waveCleared = false;
        private Texture2D creepTexture;
        private List<Creep> _creeps = new List<Creep>();
        private int _secondsToNextWave;
        private double _spawnTime;
        private SoundEffect deathsound;

        private float spawnTimer = 0;
        private List<float[]> moneyDrawData = new List<float[]>();

        public event WaveEventHandler OnWaveCleared;

        private Map map;
        private Player player;

        public int WaveID {
            get { return _waveID; }
            set { _waveID = value; }
        }
        public string Name {
            get { return _Name; }
            set { _Name = value; }
        }
        public int CreepsToSpawn {
            get { return _creepsToSpawn; }
            set { _creepsToSpawn = value; }
        }
        public bool WaveStarted {
            get { return _waveStarted; }
            set { _waveStarted = value; }
        }
        public bool WaveCleared {
            get { return _waveCleared; }
            set { _waveCleared = value; }
        }
        public List<Creep> Creeps {
            get { return _creeps; }
            set { _creeps = value; }
        }
        public int SecondsToNextWave {
            get { return _secondsToNextWave; }
            set { _secondsToNextWave = value; }
        }
        public double SpawnTime {
            get { return _spawnTime; }
            set { _spawnTime = value; }
        }
        /// <summary>
        /// Erzeugt eine neue Instanz einer Wave
        /// </summary>
        /// <param name="map">Referenz zur Map</param>
        /// <param name="creepTexture">Creep-Textur</param>
        /// <param name="ID">Wave-ID</param>
        /// <param name="waveName">Wave-Name</param>
        /// <param name="creepType">Art der Creeps (ID)</param>
        /// <param name="creepCount">Anzahl der Creeps</param>
        /// <param name="moneyPerKill">Gewinn pro Kill</param>
        /// <param name="sectonext">Sekunden bis zur nächsten Wave</param>
        public Wave(Map map, Player player, Texture2D creepTexture, int ID, String waveName, int creepType, int creepCount, int sectonext, double spawnTime, SoundEffect csound) {
            deathsound = csound;
            _waveID = ID;
            _Name = waveName;
            _creepsToSpawn = creepCount;
            _secondsToNextWave = sectonext;
            _spawnTime = spawnTime;
            this.creepTexture = creepTexture;
            this.map = map;
            this.player = player;
            for (int i = 0; i < creepCount; i++) {
                Creeps.Add(new Creep(creepType));
                if (Creeps[Creeps.Count - 1].isFlying) {
                    Creeps[Creeps.Count - 1].Path = map.FlyingPath;
                } else {
                    Creeps[Creeps.Count - 1].Path = map.DefaultPath;
                }
                Creeps[Creeps.Count - 1].OnCreepDeath += new CreepEventHandler(CreepDeathHandler);
                Creeps[Creeps.Count - 1].OnCreepSave += new CreepEventHandler(CreepSaveHandler);

            }

        }

        private void CreepDeathHandler(object sender, CreepEventArgs e)
        {
            deathsound.Play(0.4f, 0.0f, 0.0f);
            if (CreepsAlive() == 0 && OnWaveCleared != null)
            {
                _waveCleared = true;
                OnWaveCleared(this,new WaveEventArgs());
            }
            Creep c = (Creep)sender;
            player.Money += c.MoneyPerKill;
            player.KillCount++;
            moneyDrawData.Add(new float[] { 0, c.MoneyPerKill, c.Position.X, c.Position.Y });

            int direction = 0;
            switch (c.Direction)
            {
                case Direction.Up:
                    direction = 0;
                    break;
                case Direction.Left:
                    direction = 32;
                    break;
                case Direction.Down:
                    direction = 64;
                    break;
                case Direction.Right:
                    direction = 96;
                    break;
            }
            int animationframe = 0;
            if (c.AnimationCounter > 0.3) animationframe = 32;

            Globals.ParticleSystem.AddTexture(creepTexture, c.Position + Map.RenderRoot + new Vector2(16, 16), 0.35f, 0, 50, new Rectangle(direction, animationframe, 32, 32));
        }

        private void CreepSaveHandler(object sender, CreepEventArgs e) {
            player.Lives -= 1;
        }
        /// <summary>
        /// Gibt an, wieviele Creeps noch leben
        /// </summary>
        /// <returns>Anzahl der übrigen Creeps</returns>
        public int CreepsAlive() {
            int creepsAlive = 0;
            foreach (Creep creep in Creeps) {
                if (!creep.isDead) creepsAlive++;
            }
            return creepsAlive;
        }

        private void SpawnSingleCreep()
        {
            Creeps[Creeps.Count - _creepsToSpawn].Position = new Vector2(map.StartBlock.X * Block.BlockWidth, map.StartBlock.Y * Block.BlockHeight);
            Creeps[Creeps.Count - _creepsToSpawn].isSpawned = true;
        }

        /// <summary>
        /// Startet die Wave.
        /// </summary>
        /// <returns>true, wenn die Wave erfolgreich gestartet wurde, sonst false.</returns>
        public bool Start()
        {
            if (!this.WaveStarted)
            {
                UpdatePaths();
                WaveStarted = true;
                return true;
            }
            return false;
        }

        /// <summary>
        /// Aktualisiert die Pfade aller Creeps.
        /// </summary>
        public void UpdatePaths() {
            foreach (Creep creep in Creeps) {
                if (!WaveStarted || (creep.Position.X == 0 && creep.Position.Y == 0)) {
                    if (creep.isFlying) {
                        creep.Path = map.FlyingPath;
                    } else {
                        creep.Path = map.DefaultPath;
                    }
                } else {
                    if (creep.Path.Count > 1) {
                        Vector2 temp = creep.Path[0];
                        creep.Path = map.pathToEndBlock(map.GetBlock((int)temp.X, (int)temp.Y), creep.isFlying);
                        creep.Path.Insert(0, temp);
                    }
                }
            }
        }

        public void Update(GameTime gameTime) {
            if (WaveStarted) {
                if (_creepsToSpawn > 0) {
                    spawnTimer += (float)gameTime.ElapsedGameTime.TotalSeconds;
                    if (spawnTimer > _spawnTime || _creepsToSpawn == _creeps.Count) {
                        SpawnSingleCreep();
                        _creepsToSpawn--;
                        spawnTimer = 0;
                    }
                }
                for (int i = 0; i < Creeps.Count; i++) {
                    if (!Creeps[i].isDead) {
                        if (Creeps[i].Path.Count > 0) {
                            if (Creeps[i].isSpawned) {
                                Creeps[i].AnimationCounter += (float) gameTime.ElapsedGameTime.TotalSeconds;
                                if (Creeps[i].AnimationCounter > 0.6) {
                                    Creeps[i].AnimationCounter = 0;
                                }
                                if (Vector2.Distance(Creeps[i].Position, Creeps[i].Path[0] * 32) < (float)Creeps[i].Speed / 10.0) {
                                    Creeps[i].Position = Creeps[i].Path[0] * 32;
                                    Creeps[i].Path.RemoveAt(0);
                                } else {
                                    Vector2 direction = Creeps[i].Path[0] * 32 - Creeps[i].Position;
                                    direction.Normalize();
                                    if (direction == new Vector2(1, 0)) Creeps[i].Direction = Direction.Right;
                                    else if (direction == new Vector2(-1, 0)) Creeps[i].Direction = Direction.Left;
                                    else if (direction == new Vector2(0, 1)) Creeps[i].Direction = Direction.Down;
                                    else if (direction == new Vector2(0, -1)) Creeps[i].Direction = Direction.Up;
                                    Creeps[i].Position += Vector2.Multiply(direction, (float)Creeps[i].Speed/10);
                                }
                            }
                        } else {
                            Creeps[i].isDead = true;
                            CreepSaveHandler(Creeps[i], null);
                        }
                    }
                    if (CreepsAlive() == 0 && moneyDrawData.Count == 0) {
                        WaveStarted = false;
                        WaveCleared = true;
                    }
                }
                for (int i = 0; i < moneyDrawData.Count; i++) {
                    moneyDrawData[i][0] += (float)gameTime.ElapsedGameTime.TotalSeconds;
                    moneyDrawData[i][3] -= 1;
                    if (moneyDrawData[i][0] > 0.5) moneyDrawData.Remove(moneyDrawData[i]);
                }
            }
        }

        public void Draw(SpriteBatch spriteBatch, SpriteFont font) {
            foreach (Creep creep in Creeps) {
                if (!creep.isDead && creep.isSpawned) {
                    int direction = 0;
                    switch (creep.Direction) {
                        case Direction.Up:
                            direction = 0;
                            break;
                        case Direction.Left:
                            direction = 32;
                            break;
                        case Direction.Down:
                            direction = 64;
                            break;
                        case Direction.Right:
                            direction = 96;
                            break;
                    }
                    int animationframe = 0;
                    if (creep.AnimationCounter > 0.3) animationframe = 32;
                    Vector2 pos = map.RenderRoot + creep.Position;
                    spriteBatch.Draw(creepTexture, pos, new Rectangle(direction, animationframe, 32, 32), Color.White);
                    if (creep.HP < creep.MaxHP) {
                        Texture2D texture = new Texture2D(spriteBatch.GraphicsDevice, 1, 1);
                        texture.SetData<Color>(new Color[] { Color.White });
                        Rectangle lifeBarMax = new Rectangle((int)pos.X+4, (int)pos.Y, 24, 4);
                        Rectangle lifeBar = new Rectangle((int)pos.X+4, (int)pos.Y, (int)((float)creep.HP * 24 / (float)creep.MaxHP), 4);
                        spriteBatch.Draw(texture, lifeBarMax, Color.Red);
                        spriteBatch.Draw(texture, lifeBar, Color.Green);
                    }
                }
            }
            foreach (float[] data in moneyDrawData) {

                spriteBatch.DrawString(font, "+" + (int)data[1], new Vector2(data[2], data[3])+map.RenderRoot, Color.OrangeRed);
            }
        }
        public Map Map
        {
            get { return map; }
            set { map = value; }
        }
    }
}
